package manager;

import dao.FileDao;
import dao.FileMeta;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

// 针对目录进行扫描 把扫描的结果更新到数据库中
public class FileManager {
    private FileDao fileDao = new FileDao();
    // 初始化设置选手数目为1，当线程执行完所有任务之后，就立即调用依次countDown 撞线
    private CountDownLatch countDownLatch = null;
    // 用来衡量任务结束的计数器
    private AtomicInteger taskCount = new AtomicInteger(0);

    // 通过这个方法，实现针对 basePath 描述的目录，内容扫描
    // 把这里的文件和目录都扫描清楚， 并且保存到数据库中
    public void scanAll(File basePath) {
        System.out.println("[FileManager] scanAll 开始");
        long beg = System.currentTimeMillis();
        // 保证每次调用scanAll 都新建一个countDown 重新技术
        countDownLatch = new CountDownLatch(1);
        scanAllByThreadPool(basePath);
        try {
            // 如果调用依一次，调用第二次就会解除阻塞 秒表就不准了 每次调用scanAll都新建一个countDown
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("[FileManager] scanAll 结束: 执行时间：" + (end - beg) + "ms");
    }

    private void scanAllByOneThread(File basePath) {
        if(!basePath.isDirectory()){
            return;
        }

        // 针对当前目录进行扫描
        scan(basePath);

        // 列出当前目录下的所有文件
        File[] files = basePath.listFiles();
        if(files == null || files.length == 0) return;
        for (File f : files) {
            if(f.isDirectory()) {
                scanAllByOneThread(f);
            }
        }
    }
    private static ExecutorService executorService = Executors.newFixedThreadPool(8);

    private void scanAllByThreadPool(File basePath) {
        if(!basePath.isDirectory()) {
            return;
        }
        // 计数器自增
        taskCount.getAndIncrement();

        // 扫描操作放在线程池中完成
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    scan(basePath);
                } finally {
                    // 计数器自减
                    // 放在finally 确定能执行到自减
                    taskCount.getAndDecrement();
                    if(taskCount.get() == 0) {
                        // 如果计数器为0 通知主线程停表了
                        countDownLatch.countDown();
                    }
                }
            }
        });

        // 列出当前目录下的所有文件
        File[] files = basePath.listFiles();
        if(files == null || files.length == 0) return;
        for (File f : files) {
            if(f.isDirectory()) {
                scanAllByOneThread(f);
            }
        }
    }

    // scan 方法针对一个目录进行扫描 （整个遍历扫描的基本操作）
    // 当前只针对path下的文件和目录 并且更新到数据库中
    // 此方法不考虑子目录的
    public void scan(File path) {
//        System.out.println("[FileManager] 扫描路径：" + path.getAbsolutePath());
        // 列出文件系统上真实文件/目录
        List<FileMeta> scanned = new ArrayList<>();
        File[] files = path.listFiles();
        if(files != null) {
            for (File f : files) {
                scanned.add(new FileMeta(f));
            }
        }

        // 列出数据库中当前指定目录里面的内容
        List<FileMeta> saved = fileDao.searchByPath(path.getPath());

        // 找出文件系统中没有的，数据库中没有 把这些从数据库中删除
        List<FileMeta> forDelete = new ArrayList<>();
        for (FileMeta fileMeta : saved) {
            if(!scanned.contains(fileMeta)) {
                // 文件系统没有 数据库中有的 文件给单独拿出来
                forDelete.add(fileMeta);
            }
        }
        fileDao.delete(forDelete);

        // 找出文件系统有的，数据库中没有的 把这内容插入到数据库中
        List<FileMeta> forAdd = new ArrayList<>();
        for (FileMeta fileMeta : scanned) {
            if(!saved.contains(fileMeta)) {
                forAdd.add(fileMeta);
            }
        }
        fileDao.add(forAdd);
    }



}
